package cn.edu.hit.graphdata;

import cn.edu.hit.CacheManager;
import cn.edu.hit.core.Document;
import cn.edu.hit.core.db.*;
import cn.edu.hit.core.message.MessageAdapter;
import cn.edu.hit.kg.data.GraphData;
import cn.edu.hit.kg.data.Node;
import cn.edu.hit.kg.entity.BaseNode;
import cn.edu.hit.kg.entity.Fault;
import net.sf.json.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@RestController
@RequestMapping("/")
public class ConfigureController {
    private static final Logger logger = LoggerFactory.getLogger(ConfigureController.class);
    final static Map<String, Boolean> isRunMap = Collections.singletonMap("isRun", true);

    public static final CacheManager cacheManager = CacheManager.getInstance();

    static DataConnection connection = DataConnectionLoader.getInstance();
    static DataReader reader = connection.getReader();
    static DBReadHelper dbHelper = new DBReadHelper(reader);


    @Autowired
    HttpServletRequest request;

    @CrossOrigin
    @RequestMapping(method = RequestMethod.GET, value = "graph/{id}", produces = "application/json;charset=UTF-8")
    public JSONObject getEntity(@PathVariable("id") long id) {
        GraphData graphData = GraphData.getInstance();
        Node node = graphData.getById(id);
        if (node != null) {
            Set<Long> edges = graphData.getEdges().getOrDefault(node.id(), Collections.emptySet());
            JSONObject json = BaseNode.from(node).toJson();
            json.accumulate("subNodes", edges);
            return json;
        } else {
            return new JSONObject(true);
        }
    }

//    @CrossOrigin
//    @RequestMapping(method = RequestMethod.GET, value = "fault/{id}",produces="application/json;charset=UTF-8")
//    public JSONObject getFaultById(@PathVariable("id") long id){
//        GraphData graphData = GraphData.getInstance();
//        Node node = graphData.getById(id);
//        return packFaultToJsonObject(graphData, node);
//    }

    @CrossOrigin
    @RequestMapping(method = RequestMethod.GET, value = "fault/{id}", produces = "application/json;charset=UTF-8")
    public JSONObject getFaultByOutId(@PathVariable("id") String id) {
        GraphData graphData = GraphData.getInstance();
        Optional<Node> optional = graphData.getByLabel("Fault")
                .filter(node -> StringUtils.equals(id, node.get("outId").toString())).findAny();
        return packFaultToJsonObject(graphData, optional.orElse(null));
    }

    private JSONObject packFaultToJsonObject(GraphData graphData, Node node) {
        if (node != null && node.hasLabel("Fault")) {
            Set<Long> edges = graphData.getEdges().getOrDefault(node.id(), Collections.emptySet());
            JSONObject fault = BaseNode.from(node).toJson();
            fault.accumulate("subNodes", edges);
            return fault;
        } else {
            return new JSONObject(true);
        }
    }


    @CrossOrigin
    @RequestMapping(method = RequestMethod.GET, value = "rsfault/faultData/faultids/{ids}", produces = "application/json;charset=UTF-8")
    public JSONObject getFaultBySignalPoints(@PathVariable("ids") String ids) {
        try {
            String[] idArray = StringUtils.split(ids, ',');
            return packMessage200(getFaultOfPoint(idArray));
        } catch (Exception e) {
            return packMessageError(e);
        }
    }


    private List<Set<String>> getFaultOfPoint(String[] pointIds) {
        FaultPortMapping mapping = FaultPortMapping.getInstance();
        return Arrays.stream(pointIds).map(pointId -> {
            return mapping.getFpMapping().values().stream()
                    .filter(fpMapping -> fpMapping.portIds != null && fpMapping.portIds.contains(pointId))
                    .map(fpMapping1 -> fpMapping1.faultId)
                    .collect(Collectors.toSet());
        }).collect(Collectors.toList());
    }

    private JSONObject packMessage200(Object data) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.accumulate("userMessage", "获取信息成功");
        jsonObject.accumulate("internalMessage", "获取信息成功");
        jsonObject.accumulate("code", 0);
        jsonObject.accumulate("status", "SUCCESS");
        jsonObject.accumulate("moreInfo", "");
        jsonObject.accumulate("data", data);
        return jsonObject;
    }

    private JSONObject packMessageError(Throwable throwable) {
        throwable.printStackTrace();
        JSONObject jsonObject = new JSONObject();
        jsonObject.accumulate("userMessage", "获取信息失败");
        jsonObject.accumulate("internalMessage", "获取信息失败");
        jsonObject.accumulate("code", 500);
        jsonObject.accumulate("status", "EXCEPTION");
        jsonObject.put("moreInfo", throwable.getMessage());
//        jsonObject.accumulate("moreInfo",throwable.getMessage());
        return jsonObject;
    }

    public void setRequest(HttpServletRequest request) {
        this.request = request;
    }

    @CrossOrigin
    @RequestMapping(method = RequestMethod.GET, value = "rsfault/faultData/faults/{ids}", produces = "application/json;charset=UTF-8")
    public JSONObject getFaultByJoinSystemId(@PathVariable("ids") String ids) {
        String machineNum = "";
        FaultPortMapping mapping = FaultPortMapping.getInstance();
        try {
            String[] idArray = StringUtils.split(ids, ',');
            if (idArray[0] != null) {
                machineNum = idArray[0].substring(0, idArray[0].length() - 7);
            }
            logger.debug("ArraySize:" + idArray.length);

            GraphData graphData = GraphData.getInstance();

            List<Node> faults2 = graphData.getByLabel("Fault")
                    .filter(node ->
                            Stream.of(idArray)
                                    .map(id -> id.substring(id.length() - 7))
                                    .anyMatch(Predicate.isEqual(node.get("outId").toString())))
                    .collect(Collectors.toList());

            List<JSONObject> faults = new ArrayList<>();

            for (int i = 0; i < faults2.size(); i++) {
                faults.add(packJoinSystemFaultToJsonObject(graphData, faults2.get(i), machineNum));

            }

            logger.debug("FaultSize:" + faults.size());

            List<JSONObject> result = Stream.of(idArray).sequential().map(id -> {
                Optional<JSONObject> optional = faults.stream().filter(json ->
                        id.substring(id.length() - 7).equals(json.get("moduleId"))).findAny();
                if (optional.isPresent()) {
                    JSONObject jsonObject = JSONObject.fromObject(optional.get());
                    jsonObject.put("platform_id", id);
                    FaultPortMapping.FPMapping fpMapping = mapping.getFpMapping().get(id);
                    if (fpMapping != null) {
                        jsonObject.put("signalPointIds", fpMapping.portIds);
                        jsonObject.put("showType", fpMapping.showType);
                        jsonObject.put("isOpen", fpMapping.isOpen);
                    } else {
                        jsonObject.put("isOpen", false);
                    }

                    logger.debug("In Line128:" + id);
                    return jsonObject;
                } else {
                    logger.warn("Fault " + id + " not found!");
                    return null;
                }

            }).filter(x -> x != null).sorted(Comparator.comparingInt(json -> json.optBoolean("isOpen", false) ? -1 : 1)).collect(Collectors.toList());

            return packMessage200(result);
        } catch (Exception e) {
            return packMessageError(e);
        }
    }


    private JSONObject packJoinSystemFaultToJsonObject(GraphData graphData, Node node, String machineNum) {
        if (node != null && node.hasLabel("Fault")) {
            Fault faultNode = new Fault(node);

            String freqKey = "fault_reason:" + machineNum + ":" + faultNode.getId();  //每个原因的置信度应该与机器相关
            List<Map<String, Object>> reasonMatched;
            if (cacheManager.getJedis().exists(freqKey)) {
                JSONObject freq = JSONObject.fromObject(cacheManager.getJedis().get(freqKey));
                reasonMatched = BaseNode.getAllSubNodes(faultNode.getId())
                        .map(BaseNode::loadNodeById)
                        .filter(r -> r.node.hasLabel("Reason"))
                        .map(r -> {
                            Map<String, Object> map = new HashMap<>();
                            map.put("name", r.node.get("name"));
                            map.put("solution", r.node.get("solution"));
                            int quantum = freq.getInt("quantum");
                            int frequency = freq.optJSONObject("frequency").optInt("" + r.getId());
                            map.put("probability", frequency * 1.0 / quantum);
                            return map;
                        }).collect(Collectors.toList());
            } else {
                reasonMatched = BaseNode.getAllSubNodes(faultNode.getId())
                        .map(BaseNode::loadNodeById)
                        .filter(r -> r.node.hasLabel("Reason"))
                        .map(r -> {
                            Map<String, Object> map = new HashMap<>();
                            map.put("name", r.node.get("name"));
                            map.put("solution", r.node.get("solution"));
                            int quantum = Integer.parseInt("" + faultNode.node.asMap().getOrDefault("quantum", 100));
                            int frequency = 0;
                            if (faultNode.node.asMap().containsKey("frequency")) {
                                JSONObject freq = JSONObject.fromObject(faultNode.node.asMap().get("frequency"));
                                frequency = freq.optInt("" + r.getId(), 0);
                            } else {
                                map.put("probability", Double.parseDouble(r.getString("probability")));
                                return map;
                            }
                            map.put("probability", frequency * 1.0 / quantum);
                            return map;
                        }).collect(Collectors.toList());
            }


            List<String> portNames = new ArrayList<>();
            List<String> featureMatched = BaseNode.getAllSubNodes(faultNode.getId())
                    .map(BaseNode::loadNodeById)
                    .filter(r -> r.node.hasLabel("Feature"))
                    .map(feature -> {
                        return (String) feature.node.get("name");
                    })
                    .collect(Collectors.toList());
            for (int i = 0; i < featureMatched.size(); i++) {
                List<Document> channelList = reader.queryByTemplate(Filters.eq("signalType", featureMatched.get(i)));
                for (int j = 0; j < channelList.size(); j++) {
                    String channelIdStr = channelList.get(j).getString("CombineChanenels");
                    String[] IdArray = channelIdStr.substring(1, channelIdStr.length() - 1).split(",");
                    for (int k = 0; k < IdArray.length; k++) {
                        String portId = IdArray[k];
                        if (portId.length() > 5) {
                            Document portDocunment = dbHelper.getById(portId.substring(1, portId.length() - 1));
                            if (portDocunment != null) {
                                String portName = portDocunment.getString("dataSourceArgs");
                                boolean hasport = false;
                                for (int l = 0; l < portNames.size(); l++) {
                                    if (portNames.get(l).equals(portName)) {
                                        hasport = true;
                                    }
                                }
                                if (!hasport) {
                                    portNames.add(portName);
                                }
                            }
                        }
                    }
                }
            }

            List<JSONObject> reasons = new ArrayList<>();
            for (int i = 0; i < reasonMatched.size(); i++) {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("reasonName", reasonMatched.get(i).get("name"));
                jsonObject.put("solution", reasonMatched.get(i).get("solution"));
                jsonObject.put("probability", reasonMatched.get(i).get("probability"));
                reasons.add(jsonObject);
            }

            JSONObject json = new JSONObject();
            json.put("faultId", node.id());
            json.put("faultName", node.getOrDefault("name", ""));
            json.put("moduleName", node.getOrDefault("faultModule", ""));
            json.put("phenomenon", node.getOrDefault("suggest_description", ""));
            json.put("part", node.getOrDefault("location", ""));
            json.put("faultLevel", node.get("suggest"));
            json.put("suggest", node.get("suggest"));
            json.put("reasons", reasons);
            json.put("showType", 1);
            json.put("moduleId", node.get("outId"));
            json.put("signalPointIds", portNames);
            return json;
        } else {
            logger.warn("Not a valid FaultNode");
            return new JSONObject(true);
        }
    }

    public static void main(String[] args) throws IOException {
        FaultPortMapping mapping = FaultPortMapping.getInstance();
        String inputData = Files.lines(Paths.get("./fp-mapping.json")).reduce((a, b) -> a + b).get();
        mapping.loadFromJson(inputData);
        ConfigureController configureController = new ConfigureController();
//        System.out.println(configureController.getFaultByJoinSystemId("0301001031400016"));
    }
}
